using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using NLog;

namespace LDNS.Monitor
{
    /// <summary>
    /// 端口监控服务类
    /// 负责监控指定端口的状态变化
    /// 作者：huobing
    /// </summary>
    internal class PortMonitorService
    {
        private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
        private readonly int _parentProcessId;
        private readonly List<int> _portsToMonitor;
        private readonly Dictionary<int, bool> _portStatusCache;
        private readonly TimeSpan _monitorInterval;
        private CancellationTokenSource _cancellationTokenSource;
        private Task? _monitoringTask;
        private bool _isRunning;

        /// <summary>
        /// 端口状态变化事件
        /// </summary>
        public event EventHandler<PortStatusChangedEventArgs>? PortStatusChanged;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="parentProcessId">父进程ID</param>
        /// <param name="portsToMonitor">要监控的端口列表</param>
        /// <param name="monitorInterval">监控间隔（毫秒）</param>
        public PortMonitorService(int parentProcessId, List<int> portsToMonitor, int monitorInterval = 5000)
        {
            _parentProcessId = parentProcessId;
            _portsToMonitor = portsToMonitor ?? new List<int>();
            _portStatusCache = new Dictionary<int, bool>();
            _monitorInterval = TimeSpan.FromMilliseconds(monitorInterval);
            _cancellationTokenSource = new CancellationTokenSource();
            _isRunning = false;
        }

        /// <summary>
        /// 检查指定端口是否有任何实例处于开放状态
        /// </summary>
        /// <param name="port">要检查的端口号</param>
        /// <returns>如果该端口有任何实例处于开放状态，则返回true；否则返回false</returns>
        public bool AnyPortOpen(int port)
        {
            // 检查端口状态缓存
            if (_portStatusCache.TryGetValue(port, out bool status))
            {
                return status;
            }
            
            // 如果缓存中没有该端口，执行一次实时检查
            return IsPortOpen(port);
        }
        
        /// <summary>
        /// 检查所有被监控的端口中是否有任何一个处于开放状态
        /// </summary>
        /// <returns>如果有任何被监控的端口处于开放状态，则返回true；否则返回false</returns>
        public bool AnyMonitoredPortOpen()
        {
            // 检查每个被监控的端口
            foreach (var port in _portsToMonitor)
            {
                if (AnyPortOpen(port))
                {
                    return true; // 只要有一个端口开放，就返回true
                }
            }
            
            return false; // 所有被监控的端口都关闭
        }

        /// <summary>
        /// 启动监控服务
        /// </summary>
        /// <returns>是否启动成功</returns>
        public bool Start()
        {
            if (_isRunning)
            {
                _logger.Warn("监控服务已经在运行中");
                return true;
            }

            try
            {
                _cancellationTokenSource = new CancellationTokenSource();
                _monitoringTask = Task.Run(MonitorPortsAsync, _cancellationTokenSource.Token);
                _isRunning = true;
                _logger.Info("端口监控服务启动成功");
                return true;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "启动端口监控服务失败");
                _isRunning = false;
                return false;
            }
        }

        /// <summary>
        /// 停止监控服务
        /// </summary>
        public void Stop()
        {
            if (!_isRunning)
            {
                _logger.Warn("监控服务未在运行");
                return;
            }

            try
            {
                _logger.Info("正在停止端口监控服务...");
                _cancellationTokenSource.Cancel();
                _monitoringTask?.Wait(TimeSpan.FromSeconds(3)); // 等待监控任务停止
                _isRunning = false;
                _logger.Info("端口监控服务已停止");
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "停止端口监控服务时发生异常");
            }
        }

        /// <summary>
        /// 监控端口的异步方法
        /// </summary>
        private async Task MonitorPortsAsync()
        {
            try
            {
                _logger.Info($"开始监控端口，监控间隔：{_monitorInterval.TotalMilliseconds}ms");
                _logger.Info($"监控的端口列表：{string.Join(", ", _portsToMonitor)}");

                while (!_cancellationTokenSource.Token.IsCancellationRequested)
                {
                    // 检查父进程是否还在运行
                    if (!IsProcessRunning(_parentProcessId))
                    {
                        _logger.Warn($"父进程(ID: {_parentProcessId})已停止运行，监控服务将停止");
                        break;
                    }

                    // 检查每个端口的状态
                    foreach (var port in _portsToMonitor)
                    {
                        CheckPortStatus(port);
                    }

                    // 等待下一次检查
                    await Task.Delay(_monitorInterval, _cancellationTokenSource.Token);
                }
            }
            catch (TaskCanceledException)
            {
                _logger.Info("端口监控任务已取消");
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "端口监控过程中发生未处理异常");
            }
            finally
            {
                _isRunning = false;
            }
        }

        /// <summary>
        /// 检查指定端口的状态
        /// </summary>
        /// <param name="port">端口号</param>
        private void CheckPortStatus(int port)
        {
            try
            {
                bool isPortOpen = IsPortOpen(port);
                bool previousStatus;

                // 检查状态是否发生变化
                if (!_portStatusCache.TryGetValue(port, out previousStatus) || previousStatus != isPortOpen)
                {
                    _portStatusCache[port] = isPortOpen;
                    _logger.Info($"端口 {port} 状态变化: {(isPortOpen ? "开放" : "关闭").ToString()}");
                    
                    // 触发状态变化事件
                    OnPortStatusChanged(new PortStatusChangedEventArgs(port, isPortOpen));
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"检查端口 {port} 状态时发生异常");
            }
        }

        /// <summary>
        /// 检查端口是否开放
        /// </summary>
        /// <param name="port">端口号</param>
        /// <returns>端口是否开放</returns>
        private bool IsPortOpen(int port)
        {
            try
            {
                var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
                var activeTcpListeners = ipGlobalProperties.GetActiveTcpListeners();

                foreach (var listener in activeTcpListeners)
                {
                    if (listener.Port == port)
                    {
                        return true;
                    }
                }

                return false;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"检查端口 {port} 是否开放时发生异常");
                return false;
            }
        }

        /// <summary>
        /// 检查进程是否在运行
        /// </summary>
        /// <param name="processId">进程ID</param>
        /// <returns>进程是否在运行</returns>
        private bool IsProcessRunning(int processId)
        {
            try
            {
                using (var process = System.Diagnostics.Process.GetProcessById(processId))
                {
                    return process != null && !process.HasExited;
                }
            }
            catch (ArgumentException)
            {
                // 进程不存在
                return false;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"检查进程ID {processId} 是否运行时发生异常");
                return false;
            }
        }

        /// <summary>
        /// 触发端口状态变化事件
        /// </summary>
        /// <param name="e">事件参数</param>
        protected virtual void OnPortStatusChanged(PortStatusChangedEventArgs e)
        {
            PortStatusChanged?.Invoke(this, e);
        }
    }

    /// <summary>
    /// 端口状态变化事件参数类
    /// </summary>
    internal class PortStatusChangedEventArgs : EventArgs
    {
        /// <summary>
        /// 端口号
        /// </summary>
        public int Port { get; }

        /// <summary>
        /// 端口是否开放
        /// </summary>
        public bool IsOpen { get; }

        /// <summary>
        /// 事件发生时间
        /// </summary>
        public DateTime Timestamp { get; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="port">端口号</param>
        /// <param name="isOpen">端口是否开放</param>
        public PortStatusChangedEventArgs(int port, bool isOpen)
        {
            Port = port;
            IsOpen = isOpen;
            Timestamp = DateTime.Now;
        }
    }
}